<?php
error_reporting(E_ALL);
define('BASEPATH', dirname(__FILE__));
define('DS', DIRECTORY_SEPARATOR);

$config = include BASEPATH . '/config/config.php';

# 请求hash，用于写入缓存
$hash = md5($_SERVER['REQUEST_URI']);

# 缓存文件位置
$outFileName = $config['DST'] . substr($hash, 0, 2) . DS . substr($hash, 2, 2) . DS . $hash . '.' . $_GET['srac_type'];

# 文件锁
$lockFileName = $outFileName . '.lock';

# 清空缓存
if ($_SERVER['REQUEST_METHOD'] == 'PURGE') {
    if (file_exists($outFileName)) {
        @unlink($outFileName);
        echo 1;
        exit();
    } else {
        echo 0;
        exit();
    }
}

# Content-Type Header
switch ($_GET['srac_type']) {
    case 'js':
        header('Content-Type: application/x-javascript');
        break;
    case 'css':
        header('Content-Type: text/css');
        break;
}

# 默认过期1年
$noExpire = 365 * 24 * 60 * 60;
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 7200) . ' GMT');
header("Cache-Control: max-age={$noExpire}");

# 默认base路径
$basePath = '';

# 支持的charset
$charsets = $config['charset'];

# 默认charset
$charset = 'utf-8';

# 默认非import
$import = FALSE;

# import 配置文件支持的域名
$safeDomain = $config['domain'];

# 默认的expire time
$expire = 0;

# 默认的debug
$debug = FALSE;

# 分解参数
$params = explode('/', $_SERVER['REQUEST_URI']);

# 参数长度
$paramsCount = count($params);

# 分析请求参数
if (is_array($params)) {
    for ($i = 0, $num = count($params); $i < $num; $i ++) {
        switch ($params[$i]) {
            # Base Path
            case 'base':
                $i ++;
                if (checkParam($i, $paramsCount)) {
                    $basePath = str_replace('~', DS, $params[$i]) . DS;
                }
                break;
            
            # 文件数组
            case 'file':
                $i ++;
                if (checkParam($i, $paramsCount)) {
                    $file = $params[$i];
                }
                break;
            
            # 从配置文件读取
            case 'import':
                $i ++;
                if (checkParam($i, $paramsCount)) {
                    $import = TRUE;
                    $conf = $params[$i];
                }
                break;
            
            # 字符编码
            case 'charset':
                $i ++;
                if (checkParam($i, $paramsCount)) {
                    if (in_array($params[$i], $charsets)) {
                        $charset = $params[$i];
                    }
                }
                break;
            
            # 过期时间
            case 'expire':
                $i ++;
                if (checkParam($i, $paramsCount)) {
                    if (($expireTime = intval($params[$i])) > 0) {
                        $expire = $expireTime * 60;
                        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expire) . ' GMT');
                        header("Cache-Control: max-age={$expire}");
                    }
                }
                break;
            
            # debug
            case 'debug':
                $debug = TRUE;
                header("Cache-Control: private");
                break;
        }
    }
}

# 缓存文件存在,没有过期直接输出
if (file_exists($outFileName) && ($expire == 0 || (time() - filemtime($outFileName)) < $expire) && ! file_exists($lockFileName)) {
    
    header('Cached: True');
    if (checkETag($outFileName)) {
        echo file_get_contents($outFileName);
    }

} else {
    
    header('Cached: False');
    
    # 临时变量
    $tmp = '';
    
    # 通过配置引入
    if ($import) {
        $message = '';
        if (substr($conf, 0, 4) == 'http') {
            $conf = urldecode(urldecode($conf));
            $confInfo = explode('/', $conf);
            # 判断远程配置文件是否安全
            $allowed = FALSE;
            if (isset($confInfo[2])) {
                $domainInfo = explode(':', $confInfo[2]);
                $curDomain = $domainInfo[0] . '/';
                foreach ($safeDomain as $domain) {
                    if (strpos($curDomain, $domain . '/') !== FALSE) {
                        $allowed = TRUE;
                        break;
                    }
                }
            }
            # 如果allowed仍然为false，报错
            if (! $allowed) {
                show_error('Wrong configuration file ');
            }
            
            $content = getContent($message, $conf);
            if ($content === FALSE) {
                show_error("URL: {$message['url']}; CODE: {$message['http_code']}; TIME: {$message['total_time']}");
            }
            $importConfig = @simplexml_load_string($content);
        } else {
            $configPath = $config['IMPORT_PATH'] . str_replace('~', DS, $conf);
            # 配置文件不存在
            if (! file_exists($configPath)) {
                show_error("No such configuration file \n{$configPath}");
            }
            $importConfig = @simplexml_load_file($configPath);
        }
        # 配置文件xml结构错误
        if (! ($importConfig instanceof SimpleXMLElement)) {
            show_error("Config structure error!");
        }
        # 不存在file属性
        if (! isset($importConfig->files->item)) {
            show_error("Config structure error! no file property !");
        }
        if (isset($importConfig->charset) && in_array((string) $importConfig->charset, $charsets)) {
            $charset = (string) $importConfig->charset;
        }
        $configFile = array();
        if (count($importConfig->files->item) > 1) {
            # 多文件
            foreach ($importConfig->files->item as $configFileItem) {
                $configFile[] = (string) $configFileItem;
            }
            # 去重
            $configFile = array_unique($configFile);
        } else {
            # 单一文件
            $configFile[] = (string) $importConfig->files->item;
        }
        
        foreach ($configFile as $fileItem) {
            if ($debug) {
                if ($_GET['srac_type'] == 'js') {
                    $loadJS[] = array(
                        'url' => $fileItem , 
                        'charset' => $charset
                    );
                } else {
                    echo "@import url(\"{$fileItem}\") screen;\n";
                }
            } else {
                if (substr($fileItem, 0, 4) === 'http') {
                    $content = getContent($message, $fileItem);
                    if ($content === FALSE) {
                        show_error("URL: {$message['url']}; CODE: {$message['http_code']}; TIME: {$message['total_time']}");
                    }
                    $tmp .= $content . "\n";
                } else {
                    # Base 路径
                    $filePath = $config['SRC'] . $basePath;
                    # 追加文件名
                    $filePath .= str_replace('~', DS, $fileItem);
                    # 判断文件存在
                    if (! file_exists($filePath)) {
                        show_error("No such file \n{$filePath}");
                    }
                    # 数据读入临时变量
                    $tmp .= @file_get_contents($filePath) . "\n";
                }
            }
        }
        if ($debug) {
            if ($_GET['srac_type'] == 'js') {
                echo str_replace('%LOADJS%', json_encode($loadJS), @file_get_contents(BASEPATH . DS . 'tools' . DS . 'loader.js'));
            }
            exit();
        }
    
    } else {
        # 判断传入的参数 file
        if (isset($file) && $file != '') {
            # 分成数组并去重
            $files = array_unique(explode('|', $file));
        } else {
            show_error("File can't be Null");
        }
        
        # 合并数据
        foreach ($files as $fileItem) {
            # Base 路径
            $filePath = $config['SRC'] . $basePath;
            # 追加文件名
            $filePath .= str_replace('~', DS, $fileItem);
            # 判断文件存在
            if (! file_exists($filePath)) {
                show_error("No such file \n{$filePath}");
            }
            # 数据读入临时变量
            $tmp .= @file_get_contents($filePath) . "\n";
        }
    }
    # 临时文件路径
    $tmpFileName = $config['TMP'] . md5(time() . rand(100000, 999999)) . '.' . $_GET['srac_type'];
    
    # 增加签名校验
    $sign = 'Generated by FEO(http://feo.staticsdo.com)';
    $tmp .= "/*! {$sign} */";
    
    # 合并数据写入临时文件
    @file_put_contents($tmpFileName, $tmp, LOCK_EX);
    
    # 释放临时变量
    unset($tmp);
    
    # 创建目录
    makeDir(dirname($outFileName));
    
    # 压缩
    $command = "{$config['JAVACMD']}{$config['YUICOMPRESS']} --type {$_GET['srac_type']} --charset {$charset} -o {$outFileName} {$tmpFileName}";
    
    # 文件锁不存在或者锁大于5秒 执行压缩
    if (! file_exists($lockFileName) || (time() - filemtime($lockFileName)) > 5) {
        # 创建文件锁
        @file_put_contents($lockFileName, 1, LOCK_EX);
        
        # 执行压缩
        @system($command);
        
        # 语法错误会导致压缩失败。
        if (! file_exists($outFileName)) {
            show_error('syntax errors');
        }
        
        # 验证压缩后文件签名
        if (strrpos(file_get_contents($outFileName), $sign) === FALSE) {
            rename($outFileName, $outFileName . microtime(TRUE));
            show_error('Compression failure');
        }
        
        # 输出合并压缩后内容
        if (checkETag($outFileName)) {
            echo @file_get_contents($outFileName);
        }
        file_put_contents($outFileName . '.info', var_export($_SERVER, TRUE));
        # 删除临时文件
        @unlink($tmpFileName);
        
        # 删除文件锁
        @unlink($lockFileName);
    
    } else {
        show_error('File Locked');
    }

}
/**
 * 显示错误
 *
 * @param string $error
 */
function show_error ($error)
{
    header('HTTP/1.1 503 Service Unavailable');
    echo "/* 出错啦：{$error} */";
    exit();
}

/**
 * 递归创建目录
 *
 * @param string $dir
 * @param int $mode
 * @return Boolean
 */
function makeDir ($dir, $mode = 0700)
{
    if (is_dir($dir) || @mkdir($dir, $mode)) {
        //如果$dir已经存在
        //或者能直接使用mkdir函数创建
        //则使用mkdir函数创建后返回TRUE
        return TRUE;
    }
    if (! makeDir(dirname($dir), $mode)) {
        //递归创建父级目录
        //如果最后都失败了,则返回FALSE
        return FALSE;
    }
    //如果能运行到这里,说明父级目录都已经创建成功了
    return @mkdir($dir, $mode);
}

/**
 * 读取文件内容
 *
 * @param string $message
 * @param string $url
 * @return Boolean|string
 */
function getContent (&$message, $url)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    if (substr($url, 0, 5) === 'https') {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    }
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    $content = curl_exec($ch);
    if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) {
        $message = curl_getinfo($ch);
        curl_close($ch);
        return FALSE;
    }
    curl_close($ch);
    return $content;
}

# 检查param
function checkParam ($i, $count)
{
    return $i < $count;
}

# 创建ETag
function makeETag ($file)
{
    return substr(md5_file($file), 0, 16);
}

# 检测ETag
function checkETag ($file)
{
    $etag = makeETag($file);
    if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && ($_SERVER['HTTP_IF_NONE_MATCH'] == $etag || $_SERVER['HTTP_IF_NONE_MATCH'] == $etag . '-gzip')) {
        header('Etag: ' . $_SERVER['HTTP_IF_NONE_MATCH'], TRUE, 304);
        return FALSE;
    } else {
        header('Etag: ' . $etag);
        return TRUE;
    }
}